#ifndef RENDERER
#define RENDERER

#include "GE_Base.h"
namespace Syp
{
///A structure describing hardware capabilities. Useful for enquiring current hardware capabilities or setting minimum hardware required for a Pass.
struct HardwareCapabilities
{
	HardwareCapabilities():VertexShaders(false),PixelShaders(false),TextureUnits(0),CompressedTextures(false), CubeMaps(false), FBO(false), PointSprites(false){};
	///Support for VertexShaders.
    bool VertexShaders;
	///Support for PixelShaders.
	bool PixelShaders;
	///Number of TextureUnits available on hardware.
	PShort TextureUnits;
	///Support for compressed Textures
	bool CompressedTextures;
	bool CubeMaps;
	///Support for FBOs
	bool FBO;
	///Support for Point Sprites
	bool PointSprites;
	
	//...etc
};
///The Rendering core.Renderer is a singleton as there should only ever be 1 instance of a Rendering system.
class SYPAPI Renderer
{
public:
	///Draws a frame
	void run();
	///Returns a reference to the Rendering system
	inline static Renderer& getRenderer()
	{
		assert(rnd && "Renderer cannot be returned until it has been initialised!");
		return (*rnd);
	};	
	inline int getWidth()
	{
        return Width;
	};

	inline int getHeight()
	{
        return Height;
	};
	inline RenderMode getCurrMode()
	{
		return currMode;
	};
	///Updates a mesh contents by sending it to gfx card, user has to manually call	updateMesh should he make any changes after addMesh().
	void updateMesh(BaseMesh* m);
	void updateIndices(IndexedMesh* m);
	///Removes a mesh/all instances as well. Deletes memory used by Mesh as well.
	void removeMesh(BaseMesh* m);
	///Attaches the bufferobject to the FBO
	void attachBuffer(FrameBufferObject* fbo, BufferObject* bo);
	///Attaches a TextureObj to the FBO
	void attachTexture(FrameBufferObject* fbo, TextureObj* tex);
	///Binds the FBO
	void bindFBO(FrameBufferObject* fbo);
	void unBindFBO();


	///Binds an IndexedMeshes attribute buffers and which buffers to bind. 
	void bindMeshAttribBuffers(IndexedMesh* im, bool bindAttribData, bool bindAttribDatapb);

	///HighLevel draw function which does all setup and drawing of a instance.
	void draw(MeshInstance* m, PInt drawCall, Pass* p);	

	///Releases the Renderer
	static void release();	
	///Initialises Renderer
	static void initRenderer(int width, int height);		
	///Mesh specific drawing methods
	void drawIndexed(IndexedMesh* m);
	void drawNIndexed(NIndexedMesh* m);

	void clearColorDepthBuffer();
	void clearDepthBuffer();
	void clearColorBuffer();

	///Reads back pixels of type and stores it in data
	void readPixels(BufferObjectType type, PInt x, PInt y, PInt w, PInt h, float* data);

	///Sorting of instances based on sorting mode.
	//void doTextureSort();
	///Renderer decides best way to transform mesh after it matrices have been calculated. If VertexPrograms are supported, vertices are transformed on the graphics card. User should not call this method. NOTE: Could be ported to Pass.
	void AnimateBoneMesh(IndexedMesh* m);
	///Swaps buffers. Should be called at the end of everything by the Engine only. Users should call SypEngine::EndFrame()
	void swapBuffers();
	///You can define a rectangular portion of your window and restrict drawing to take place within it.
	void setScissorsRegion(int x, int y, int width, int height);
	///Disables all color writes to the Framebuffer or currently bound FBO
	void disableColorWrites();
	///Disables all color reads from the Framebuffer or currently bound FBO
	void disableColorReads();
	///Generates a texture object in VRAM
	void generateTexObj(TextureObj* to);
	///Generates a FBO object with the following width/height
	FrameBufferObject* generateFBOObj(PInt w, PInt h);
	///Generates a BufferObject of type(Depth/stencil/etc..) and returns a pointer to it
	BufferObject* generateBufferObj(BufferObjectType Type, PInt width, PInt height);	
	///Sets the background clear Color
	void setClearColor(Color3f& c,float alpha = 1.0f);
	///Goes into Ortho mode
	void setUpOrtho();
	///Goes into Perspective mode
	void setUpPerspective();
	///Setsup scene cameras, this function is where the camera is actually setup
	void setUpCamera();
	///Sets the camera to be used for rendering the scene. Camera is setup again when this function is called.
	void setActiveCamera(Camera* cam);
	///Tells the renderer to bind a texture. Renderer double-checks if texture is already bound to avoid unnecessary expensive texture switch.
	void bindTexture(TextureObj* textureID);
	///Binds a Material.
	void bindMaterial(Material* mat);
	///Binds a SubMaterial.
	void bindSubMaterial(SubMaterial& mat);
	///Tells the renderer to delete this texture from VRAM
	void removeTexture(TextureObj* to);
	///Fills TextureObj with the image information contained in data. Format/size of data should be correctly contained in the TextureObj.
	///bool now indicates if this should be done immediately or it may be delayed 
	void fillTexObj(TextureObj* to, unsigned char* data, bool now = false);
	///Does required pending VRAM submissions. Automatically called by SypEngine every frame.
	void doAdmin();

	///Does all the setting up of Lights based on numLights value.
	void setUpLights(PInt numLights);

	///Enables point sprites. Requires that vertices be sent as POINTS.
	void enablePointSprites();
	///Disables point sprites
	void disablePointSprites();
	///Sets the point size, used when rendering Points
	void setPointSpritesSize(float size);
	///Enables Depth Testing
	void enableDepthTest();
	///Disables Depth Testing
	void disableDepthTest();

	///Shader Stuff
	///Compiles a vertex shader.
	void compileVertexShader(VertexShader* vs);
	///Compiles a pixel shader.
	void compilePixelShader(PixelShader* ps);
	///Links all shaders in a ShaderProgram
	void linkShaders(ShaderProgram* sp);
	///Creates and returns a VertexShader handle
	PInt generateVertexShader();
	///Creates and returns a PixelShader handle
	PInt generatePixelShader();
	///Creates and returns a ShaderProgram handle
	PInt generateShaderProgram();
	///Attaches a Shader to a ShaderProgram
	void attachShaderToProgram(Shader* s,ShaderProgram* sp);	
	///Deletes a shader and removes it.
    void deleteShader(Shader* s);
	///Deletes a ShaderProgram
	void deleteShaderProgram(ShaderProgram* sp);
	///Deletes/Frees up the FBO. Not to be called by user, use FrameBufferObject::destroyFrameBufferObject
	void deleteFrameBufferObject(FrameBufferObject* fbo);
	///Sets up a 4X4 matrix.
	void setUniformMatrix4f(String& uniformName, PInt num, float* val);
	///Sets up a vec4 uniform. Can only be called once the ShaderProgram is in use. num specifies the number of vec4s are specified in the name variable in the shader.
	void setUniform4f(String& uniformName, PInt num, float* val);
	///Sets up vec4 attributes per vertex.
	//@attribName refers to variable name in shader
	//@size refers to number of components per vertex
	//@normalised refers to whether the data should be clamped in the range -1 to 1 or 0 to 1(unsigned values).
	//@spacing between elements
	void setAttributef(String& attribName, PInt size, bool normalised, PInt stride, float* val);
	///Sets up vec4 attributes per vertex.
	//@attribName refers to variable name in shader
	//@size refers to number of components per vertex
	//@normalised refers to whether the data should be clamped in the range -1 to 1 or 0 to 1(unsigned values).
	//@spacing between elements
	void setAttributei(String& attribName, PInt size, bool normalised, PInt stride, PByte* val);
	///Disables vertex attribute for current Pass shader
	void unSetAttribute(String& attribName);
	///Returns the Mesh currently being drawn.Intended for use by Passes.
	BaseMesh* getCurrentMesh();
	///Returns the MeshInstance currently being drawn.Intended for use by Passes.
	MeshInstance* getCurrentMI();
	///Returns the current Pass in use by the RenderSystem.
	Pass* getCurrentPass();
	///Sets the global ambient value which will affect all objects in the scene including those not being lit by lights.
	void setGlobalAmbience(const Point4& globalAmb);
	///Sets up a light in the scene
	void setUpLight(Light* l);
	///Returns a const ref to the current HardWare's capabilities.
	const HardwareCapabilities& getHardwareCpbRef();
	///called when ever window size changes
	static void resizeWindow(int width,int height);
	///Returns the number of polygons drawn in the previous frame
	PInt getNumPolyDrawn();
	///Updates number of polys drawn variable(NOT TO BE CALLED BY USER)
	void updatePolyDrawn();

protected:
	///Friend functions are required to prevent or reduce the chances of the user doing something stupid with them.
	///For generating VBOs and keeping GL calls in Renderer class. User should not call    Renderer::addMesh, should only be called by MeshManager's friend functions. 
	///VBOs	are removed by their Mesh destructors which call removeFromVRAM().
	friend class NIndexedMesh;
	friend class IndexedMesh;

	friend void IndexedMesh::removeFromVRAM();
	friend void NIndexedMesh::removeFromVRAM();


	///Deletes VRAM used by mesh, should be called only by mesh's destructor
	void removeMeshFromVRAM(IndexedMesh* m);
	void removeMeshFromVRAM(NIndexedMesh* m);
	
	///NOT TO BE CALLED BY USER, all meshes are added on creation.
	void addMesh(IndexedMesh* m);
	///NOT TO BE CALLED BY USER, all meshes are added on creation.
	void addMesh(NIndexedMesh* m);
	
	///Calculates memory required by mesh for VBO
	PInt calcVBOsize(BaseMesh* m);	
	///Setups blending
	void setUpBlendType(BlendType b);

	///Pointer to the active camera
	Camera* activeCamera;

	

	


	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////	
	///ID of texture for fonts
	//PInt fontTexture;
	///Checks if a particular extension is supported
	bool checkExtension(std::string item);
	///Stores the extensions supported by user's gfx card
	String extensions;
	///returns the extensions supported by the Gfx card
	const char* Renderer::getExtensions();	
	///Sets up the Viewport for rendering
	void setUpViewport();
	///Sets up default OpenGL states and other initialisation
	void initGL();
	///Horizontal Resolution of screen
	int Width;
	///Vertical Resolution of screen
	int Height;		
private:
	///Universal Shader compiler(OpenGL). Different higher levels compiler functions for vertex/pixel shaders exist because not all apis might be able to universally compile like OpenGL.
	void compileShader(Shader* vs);
	SYPMutex TextureSubmissionMutex;
	///List of TextureSubmissions Renderer has to make
	std::list<TextureObj*> TextureSubmissionList;
	///The actual texture data for submission.
	std::list<unsigned char*> TextureDataSubmission;
	///Optimisation to prevent 8 bytes of unecessary transfer to IndexedMesh then back to Renderer.
	PInt currDrawCall;
	///Constructor of Renderer is private so that user cannot new it
	Renderer();
	///Destructor of Renderer is private so that user cannot delete it
	~Renderer();
	///The renderer Object
	static Renderer* rnd;
	///Currently bound texture
	TextureObj* boundTexture;
	///Currently bound Material
	Material* boundMaterial;

	//Number of polys drawn in previous frame
	PInt polyDrawn;
	PInt polyCount;//counter to count polys for this frame

	////////////Lighting Stuff/////////////	
	PByte currActiveLights;	

	RenderMode currMode;
	BlendType currBlendMode;
	Pass* currPass;

	BaseMesh* currentMesh;
	MeshInstance* currentMI;

	///OpenGL Extensions definition

	HardwareCapabilities gfxcard;
	Color3f clearColor;

	/// Sets up glVertexPointer/glNormalPointer/etc.. based on VertexType
	void setUpVertexPointers(BaseMesh* m);	
	inline void unSetVertexPointers();

	
};
}
#endif

